package pdf.IText;

import cn.hutool.core.io.resource.ClassPathResource;
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.html2pdf.attach.impl.OutlineHandler;
import com.itextpdf.kernel.pdf.WriterProperties;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.element.AreaBreak;
import com.itextpdf.layout.element.IBlockElement;
import com.itextpdf.layout.element.IElement;
import com.itextpdf.layout.properties.AreaBreakType;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.font.FontProvider;
import freemarker.template.Configuration;
import freemarker.template.TemplateExceptionHandler;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import java.io.*;
import java.util.List;
import java.util.Map;


/**
 * @author zgj
 * @create 2023-01-09 13:54
 * @description
 */
public class ITextGenPDF {

    private ITextGenPDF() { }

    /**
     * 多线程操作
     */
    private volatile static Configuration configuration;

    static {
        if (configuration == null) {
            synchronized (ITextGenPDF.class) {
                if (configuration == null) {
                    configuration = new Configuration(Configuration.VERSION_2_3_28);
                }
            }
        }
    }

    /**
     * freemarker 通过流的方式 引擎渲染 html
     *
     * @param dataMap     传入 html 模板的 Map 数据
     * @param ftlFilePath html 模板文件相对路径(相对于 resources路径,路径 + 文件名)，之后通过 BufferedReader 流来读取模板
     *                    eg: "templates/pdf_export_demo.ftl"
     * @return
     */
    public static String freemarkerRender(Map<String, Object> dataMap, String ftlFilePath) {
        configuration.setDefaultEncoding("UTF-8");
        configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        configuration.setLogTemplateExceptions(false);
        configuration.setWrapUncheckedExceptions(true);
        Writer out = new StringWriter();
        try {
            Template template = new Template("", ITextGenPDF.returnReaderStream(ftlFilePath), configuration);
            template.process(dataMap, out);
            out.flush();
            return out.toString();
        } catch (IOException | TemplateException e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 使用 iText 生成 PDF 文档
     *
     * @param htmlTmpStr   html 模板文件字符串
     * @param fontFilePath 所需字体文件(相对路径+文件名)
     * @param waterImgPath 水印图片路径
     * @param waterContent 水印文字内容
     */
//    public static byte[] createPDF(String htmlTmpStr, String fontFilePath, String waterImgPath, String waterContent) {
    public static byte[] createPDF(String htmlTmpStr) {

        byte[] result = null;
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        PdfWriter writer = new PdfWriter(outputStream, new WriterProperties().setFullCompressionMode(Boolean.TRUE));
        PdfDocument doc = new PdfDocument(writer);
        try {
//            String waterText = "水印";
//            if (StringUtils.isNotBlank(waterContent)) {
//                String[] split = waterContent.split("-");
//                waterText = split[0] + "-" + split[1];
//            }

            doc.setDefaultPageSize(PageSize.A5);
            FontProvider fontProvider = new FontProvider();

            // 设置中文字体文件的路径，可以在classpath目录下
//            fontProvider.addFont(fontFilePath, PdfEncodings.IDENTITY_H);
            fontProvider.addSystemFonts();

            // 获取字体，提供给水印 和 页码使用
            PdfFont pdfFont = fontProvider
                .getFontSet()
                .getFonts()
                .stream()
                .findFirst()
                .map(fontProvider::getPdfFont)
                .orElse(null);
            // 添加页眉
//            doc.addEventHandler(PdfDocumentEvent.START_PAGE, new PdfHeaderMarker(pdfFont, "页眉"));
            // 添加水印
//            doc.addEventHandler(PdfDocumentEvent.INSERT_PAGE, new PdfWaterMarker(pdfFont, waterText, waterImgPath));
            // 添加页脚
//            doc.addEventHandler(PdfDocumentEvent.END_PAGE, new PdfPageMarker(pdfFont));

            ConverterProperties properties = new ConverterProperties();
            fontProvider.addStandardPdfFonts();
            fontProvider.addSystemFonts();
            properties.setFontProvider(fontProvider);

            // PDF目录
            properties.setOutlineHandler(OutlineHandler.createStandardHandler());

            // 将html转为pdf代码块，按div生成每天一页pdf
            Document document = new Document(doc);
            List<IElement> iElements = HtmlConverter.convertToElements(htmlTmpStr, properties);
            int size = iElements.size();
            for (int i = 0; i < size; i++) {
                IElement iElement = iElements.get(i);
                document.add((IBlockElement) iElement);
                if (!(i == size - 1)) { // 不是最后一页
                    // 添加新的一页
                    document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
                }
            }
            document.close();
            result = outputStream.toByteArray();
        } finally {
            try {
                doc.close();
                writer.close();
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 根据文件相对路径返回一个 BufferedReader 流
     *
     * @param filePath
     * @return
     * @throws IOException
     */
    public static BufferedReader returnReaderStream(String filePath) throws IOException {
        return new BufferedReader(new InputStreamReader(new ClassPathResource(filePath).getStream()));
    }

    /**
     * 根据文件相对路径返回一个 BufferedReader 流
     *
     * @param filePath
     * @return
     * @throws IOException
     */
    public static InputStream returnInputStream(String filePath) throws IOException {
        return new ClassPathResource(filePath).getStream();
    }
}